Redis API的使用和理解

Redis API的使用和理解

  • 通用命令
  • 字符串类型
  • 哈希类型
  • 列表类型
  • 集合类型
  • 有序集合类型

通用命令和数据结构

通用命令

keys

  • 遍历所有的key

    API:

    keys [pattern]
  • keys命令一般不在生产环境使用

    • keys是一个比较重的命令, 复杂度为O(n), Redis是一个单线程的, 所以此命令会阻塞其他命令
    • 本身在生产环境中使用此命令没有太大意义, 如若确有此需求 可以使用scan命令
  • keys * 怎么用

    • 热备从节点 (此节点一般不会部署到生产环境中)
    • scan
  • 演示1:

    [root@localhost soft]# redis-cli -h 127.0.0.1 -p 6379
    127.0.0.1:6379> set hello world
    OK
    127.0.0.1:6379> set name Jiavg
    OK
    127.0.0.1:6379> set java best
    OK
    127.0.0.1:6379> keys *
    1) "name"
    2) "hello"
    3) "java"
    127.0.0.1:6379> dbsize
    (integer) 3
  • 演示2:

    127.0.0.1:6379> mset hello world hehe haha php good phe his
    OK
    127.0.0.1:6379> keys he*
    1) "hehe"
    2) "hello"
    127.0.0.1:6379> keys he[a-h]*
    1) "hehe"
    127.0.0.1:6379> keys ph?
    1) "phe"
    2) "php"

dbsize

  • 计算key的总数

    API:

    dbsize
    • dbsize可以在线上使用, Redis内置一个计数器, 会实时更新, 故其时间复杂度为O(1)
  • 演示

    127.0.0.1:6379> dbsize
    (integer) 0
    127.0.0.1:6379> mset k1 v1 k2 v2 k3 v3 k4 v4
    OK
    127.0.0.1:6379> dbsize
    (integer) 4
    127.0.0.1:6379> sadd a b c d
    (integer) 3
    127.0.0.1:6379> dbsize
    (integer) 5

exists

  • 检查key是否存在

    API:

    exists key1 [key2 ...]
    • 存在: 返回1, 不存在: 返回0

    • 一般情况下可以随意使用, 时间复杂度为O(1), 但是存在特殊情况, 后面将会介绍

  • 演示:

    127.0.0.1:6379> dbsize
    (integer) 0
    127.0.0.1:6379> set k1 v1
    OK
    127.0.0.1:6379> exists k1
    (integer) 1
    127.0.0.1:6379> del k1
    (integer) 1
    127.0.0.1:6379> exists k1
    (integer) 0

del

  • 删除指定的key-value

    API:

    del key1 [key2 ...]
    • 删除成功: 返回1, 删除失败: 返回0
  • 演示:

    127.0.0.1:6379> dbsize
    (integer) 0
    127.0.0.1:6379> mset k1 v1 k2 v2
    OK
    127.0.0.1:6379> get k1
    "v1"
    127.0.0.1:6379> del k1
    (integer) 1
    127.0.0.1:6379> get k1
    (nil)
    127.0.0.1:6379> del k3
    (integer) 0

expire, ttl, persist

  • expire

    API:

    # 除了指定时间间隔, 也可以指定时间戳
    expire key seconds
    • 指定key过期时间
  • ttl

    API:

    ttl key
    • 查看key剩余过期时间
      • 返回-2: 代表key已经不存在
      • 返回-1: 代表key存在, 并且没有过期时间
      • 返回正整数: 代表key距离过期时间的秒数
  • persist

    API:

    persist key
    • 去掉key的过期时间
  • 示例:

    127.0.0.1:6379> dbsize
    (integer) 0
    127.0.0.1:6379> set k1 v1
    OK
    127.0.0.1:6379> expire k1 10
    (integer) 1
    127.0.0.1:6379> ttl k1
    (integer) 6
    127.0.0.1:6379> ttl k1
    (integer) 2
    127.0.0.1:6379> ttl k1
    (integer) -2
    127.0.0.1:6379> ttl k1
    (integer) -2
    127.0.0.1:6379> set k2 v2
    OK
    127.0.0.1:6379> expire k2 10
    (integer) 1
    127.0.0.1:6379> persist k2
    (integer) 1
    127.0.0.1:6379> ttl k2
    (integer) -1

type

  • 返回key的类型

    API:

    type key
    • 返回值有: string, hash, list, set, zset, none(代表key不存在)
  • 演示:

    127.0.0.1:6379> dbsize
    (integer) 0
    127.0.0.1:6379> set k1 v1
    OK
    127.0.0.1:6379> type k1
    string
    127.0.0.1:6379> sadd myset a b c
    (integer) 3
    127.0.0.1:6379> type myset
    set

时间复杂度

2020-04-14_175048

数据结构和内部编码

2020-04-14_200442

Redis Object

2020-04-14_200859

线程模型

单线程

2020-04-14_201230

单线程为什么这么快?

  1. 纯内存
  2. 非阻塞IO
  3. 避免线程切换和竞态消耗

2020-04-14_201701

注意事项

  • 一次只运行一条命令

  • 拒绝长慢命令

    (keys, flushall, flushdb, slow lua script, mutil/exec, operate big value (collection)

  • 其实不是单线程

    redis也是多线程的, 但是redis的主线程处理业务, 而其他三个线程跟主要功能是关系不到的.

    redis为了保证其高效, 一些比较耗时的动作会起线程或者进程来完成, 不会阻塞在业务主线程上.

    (例如: fysnc file descriptor 和 close file descriptor会开起新线程)

字符串

字符串键值结构

2020-04-14_203442

字符串使用场景

  • 缓存
  • 计数器
  • 分布式锁
  • ……

get, set, del

# 获取key对应的value   O(1)时间复杂度
get key

# 设置key-value       O(1)时间复杂度
set key value

# 删除key-value       O(1)时间复杂度
del key
  • 演示:
127.0.0.1:6379> dbsize
(integer) 0
127.0.0.1:6379> set hello "world"
OK
127.0.0.1:6379> get hello
"world"
127.0.0.1:6379> del hello
(integer) 1
127.0.0.1:6379> get hello
(nil)

incr, decr, incrby, decrby

# key自增1, 如果key不存在, 自增后get(key)=1   O(1)时间复杂度
incr key

# key自减1, 如果key不存在, 自增后get(key)=-1  O(1)时间复杂度
decr key

# key自增k, 如果key不存在, 自增后get(key)=k   O(1)时间复杂度
incrby key k

# key自减k, 如果key不存在, 自增后get(key)=-k  O(1)时间复杂度
decrby key k
  • 演示:
127.0.0.1:6379> get counter
(nil)
127.0.0.1:6379> incr counter
(integer) 1
127.0.0.1:6379> get counter
"1"
127.0.0.1:6379> incrby counter 99
(integer) 100
127.0.0.1:6379> decrby counter 50
(integer) 50
127.0.0.1:6379> decr counter
(integer) 49

实战

  • 实现如下功能:

    记录网站每个用户个人主页的访问量?

    incr userid:pageview (单线程, 无竞争)

  • 实现如下功能:

    缓存视频的基本信息 (数据源在MySQL中)伪代码?

    2020-04-14_210146

  • 实现如下功能:

    分布式id生成器?

    incr id (原子操作 [基础思路, 实际操作较为复杂])

    2020-04-14_210321

set, setnx, set xx

# 不管key是否存在, 都设置    O(1)时间复杂度
set key value

# key不存在, 才设置         O(1)时间复杂度
setnx key value

# key存在, 才设置           O(1)时间复杂度
set key value xx

# 将value关联到key, 并将key的生存时间设为seconds(以秒为单位)。
# 将set命令和expire命令结合为原子操作, 在做一些分布式锁方面很有用
# O(1)时间复杂度
setex key seconds value  
  • 演示:
127.0.0.1:6379> exists name
(integer) 0
127.0.0.1:6379> set name Jiavg
OK
127.0.0.1:6379> setnx name lylgjiavg
(integer) 0
127.0.0.1:6379> setnx username jiavag
(integer) 1
127.0.0.1:6379> set name root xx
OK
127.0.0.1:6379> set name1 root xx
(nil)
127.0.0.1:6379> setex school 10 lylg
OK
127.0.0.1:6379> ttl school
(integer) 4

mget, mset

# 批量获取key, 原子操作     时间复杂度O(n): n为获得key的数量
mget key1 key2 key3 [key4...]

# 批量设置key-value        时间复杂度O(n): n为获得key的数量
mset key1 value1 key2 value2 key3 value3
  • 示例:
127.0.0.1:6379> mset name jiavg school lylg addr henan
OK
127.0.0.1:6379> mget name school addr
1) "jiavg"
2) "lylg"
3) "henan"
  • n次get

2020-04-14_213018

  • 1次mget

2020-04-14_213100

getset, append, strlen

# set key newvalue 并返回旧的value    O(1)时间复杂度
getset key newvalue

# 将value追加到旧的value              O(1)时间复杂度
append key value

# 返回字符串的长度 (注意中文)           O(1)时间复杂度
strlen key
  • 示例
127.0.0.1:6379> set name jiavg
OK
127.0.0.1:6379> getset name lylgjiavg
"jiavg"
127.0.0.1:6379> append name ", hi!"
(integer) 14
127.0.0.1:6379> get name
"lylgjiavg, hi!"
127.0.0.1:6379> strlen name
(integer) 14
127.0.0.1:6379> set hello "你好"
OK
127.0.0.1:6379> strlen hello
(integer) 6

incrbyfloat, getrange, setrange

# 增加key对应的浮点值                O(1)时间复杂度
incrbyfloat key floatvalue

# 获取字符串指定下标所有的值           O(1)时间复杂度
getrange key start end

# 设置指定下标所有对应的值             O(1)时间复杂度
setrange key index value
  • 示例:
127.0.0.1:6379> set counter 1
OK
127.0.0.1:6379> incrbyfloat counter 1.5
"2.5"
127.0.0.1:6379> get counter
"2.5"
127.0.0.1:6379> set username lylgjiavg
OK
127.0.0.1:6379> getrange username 0 3
"lylg"
127.0.0.1:6379> setrange username 7 n
(integer) 9
127.0.0.1:6379> get username
"lylgjiang"

字符串总结

2020-04-14_215422

哈希

哈希键值结构

2020-04-14_215737

2020-04-14_215809

特点

  • Mapmap (即map中含有map的结构)
  • field不能相同, value可以相同

hget, hset, hdel

# 获取hash key 对应的field的value      O(1)时间复杂度
hget key field

# 设置hash key对应的field的value       O(1)时间复杂度
hset key field value

# 删除hash key对应field的value         O(1)时间复杂度
hdel key field
  • 示例:
127.0.0.1:6379> hset user:1:info name jiavg
(integer) 1
127.0.0.1:6379> hset user:1:info age 22
(integer) 1
127.0.0.1:6379> hgetall user:1:info
1) "name"
2) "jiavg"
3) "age"
4) "22"
127.0.0.1:6379> hdel user:1:info age
(integer) 1
127.0.0.1:6379> hgetall user:1:info
1) "name"
2) "jiavg"

hexists, hlen

# 判断hash key是否有field        O(1)时间复杂度
hexist key field

# 获取hash key field的数量       O(1)时间复杂度
hlen key
  • 示例:
127.0.0.1:6379> hgetall user:1:info
1) "name"
2) "jiavg"
3) "age"
4) "22"
127.0.0.1:6379> hexists user:1:info age
(integer) 1
127.0.0.1:6379> hlen user:1:info
(integer) 2

hmget, hmset

# 批量获取hash key的一批field对应的值   时间复杂度O(n): n为获得key的数量
hmget key field1 field2 [field3...]

# 批量设置hash key的一批field value    时间复杂度O(n): n为获得key的数量
hmset key field1 value1 field2 value2 [field3 value3...]
  • 示例
127.0.0.1:6379> hmset user:1:addr addr1 china addr2 henan addr3 zhoukou
OK
127.0.0.1:6379> hmget user:1:addr addr1 addr3
1) "china"
2) "zhoukou"

实战

  • 实现如下功能:

    记录网站每个用户个人主页的访问量?

    hincrby user:1:info pageview count

  • 实现如下功能:

    缓存视频的基本信息(数据源在MySQL中)伪代码?

2020-04-14_222420

hgetall, hvals, hkeys

# 返回hash key对应的所有field和value
# 时间复杂度O(n): n为获得key的数量
hgetall key

# 返回hash key对应的所有field的value
# 时间复杂度O(n): n为获得key的数量
hvals key

# 返回hash key对应所有的field
# 时间复杂度O(n): n为获得key的数量
hkeys key
  • 示例:
127.0.0.1:6379> hgetall user:1:info
1) "name"
2) "jiavg"
3) "age"
4) "22"
127.0.0.1:6379> hvals user:1:info
1) "jiavg"
2) "22"
127.0.0.1:6379> hkeys user:1:info
1) "name"
2) "age"

小心使用hgetall

  • 小心使用, 牢记单线程
  • 如果hgetall的key的field很多将会造成其他命令阻塞

字符串 vs 哈希

  • 相似的API

2020-04-14_223836

如何存储用户属性?

用户信息 (string v1 实现)

2020-04-14_223959

用户信息 (string v2 实现)

2020-04-14_224048

用户信息 (hash 实现)

2020-04-14_224139

三种方案比较

2020-04-14_224224

列表

列表结构

2020-04-14_224647

2020-04-14_224714

特点

  • 有序
  • 可以重复
  • 左右两边插入弹出

rpush, lpush

# 从列表右端插入值 (1~n个)    时间复杂度O(1~n)
rpush key value1 value2 [value3...]

# 从列表左端插入值 (1~n个)    时间复杂度O(1~n)
lpush key value1 value2 [value3...]

2020-04-14_224955

2020-04-14_225017

linsert

# 在list指定的值前|后插入newvalue
# 时间复杂度O(n)
linsert key before|after value newvalue

lpop, rpop

# 从列表左侧弹出一个item
# 时间复杂度O(1)
lpop key

# 从列表右侧弹出一个item
# 时间复杂度O(1)
rpop key

lrem

2020-04-14_231707

ltrim

# 按照索引范围修剪列表
# 时间复杂度O(n)
ltrim key start end

2020-04-14_232017

lrange

2020-04-14_232246

lindex

# 获取列表指定索引的item
# 时间复杂度O(n)
lindex key index

llen

# 获取列表长度
# 时间复杂度O(1)
llen key

lset

# 设置列表指定索引值为newvalue
# 时间复杂度O(n)
lset key index newvalue

演示

127.0.0.1:6379> lpush mylist a b c 
(integer) 3
127.0.0.1:6379> rpush mylist d e f
(integer) 6
127.0.0.1:6379> lrange mylist 1 -1
1) "b"
2) "a"
3) "d"
4) "e"
5) "f"
127.0.0.1:6379> lrange mylist 0 -1
1) "c"
2) "b"
3) "a"
4) "d"
5) "e"
6) "f"
127.0.0.1:6379> lpop mylist
"c"
127.0.0.1:6379> rpop mylist
"f"
127.0.0.1:6379> lrange mylist 0 -1
1) "b"
2) "a"
3) "d"
4) "e"
127.0.0.1:6379> linsert mylist before a a1
(integer) 5
127.0.0.1:6379> lrange mylist 0 -1
1) "b"
2) "a1"
3) "a"
4) "d"
5) "e"
127.0.0.1:6379> lrem mylist 0 a
(integer) 1
127.0.0.1:6379> lrange mylist 0 -1
1) "b"
2) "a1"
3) "d"
4) "e"
127.0.0.1:6379> ltrim mylist 1 3
OK
127.0.0.1:6379> lrange mylist 0 -1
1) "a1"
2) "d"
3) "e"
127.0.0.1:6379> lindex mylist 0
"a1"
127.0.0.1:6379> llen mylist
(integer) 3
127.0.0.1:6379> lset mylist 0 a
OK
127.0.0.1:6379> lrange mylist 0 -1
1) "a"
2) "d"
3) "e"

blpop, brpop

# lpop阻塞版本, timeout是阻塞超时时间, timeout=0为永远不阻塞
# 时间复杂度O(1)
blpop key timeout

# rpop阻塞版本, timeout是阻塞超时时间, timeout=0为永远不阻塞
# 时间复杂度O(1)
brpop key timeout

TIPS

  1. lpush + lpop = Stack
  2. lpush + rpop = Queue
  3. lpush + ltrim = Capped Collection
  4. lpush + brpop = Message Queue

集合

集合结构

2020-04-14_234554

2020-04-14_234905

特点

  • 无序
  • 无重复
  • 支持集合间操作

sadd, srem

# 向集合key添加1element(如果element已经存在, 添加失败)
# 时间复杂度O(1)
sadd key element

# 将集合key中的element移除掉
# 时间复杂度O(1)
srem key element

scard, sismember, srandmember, spop, smembers

  • 小心使用smembers

2020-04-14_235407

演示

127.0.0.1:6379> sadd names jiavg znc lx ycg
(integer) 4
127.0.0.1:6379> smembers names
1) "lx"
2) "ycg"
3) "jiavg"
4) "znc"
127.0.0.1:6379> spop names
"lx"
127.0.0.1:6379> smembers names
1) "ycg"
2) "jiavg"
3) "znc"
127.0.0.1:6379> scard names
(integer) 3
127.0.0.1:6379> sismember names lx
(integer) 0

实战

  • 抽奖系统

2020-04-15_000013

  • Like, 赞, 踩

2020-04-15_000056

  • 标签

2020-04-15_000142

集合间API (sdiff, sinter, sunion)

2020-04-15_000408

集合间实战

  • 共同关注
2020-04-15_000531

TIPS

  • sadd = tagging
  • spop/srandmember = random item
  • sadd + sinter = social graph

有序集合

有序集合结构

2020-04-15_001535

集合 vs 有序集合

2020-04-15_001429

列表 vs 有序集合

2020-04-15_001706

zadd

# 添加score和element, element不可重复, score可以重复(可以多对)
# 时间复杂度O(logN)
zadd key score element 

zrem

# 删除元素 (可以是多个) 
# 时间复杂度O(1)
zrem key element

zscore

# 返回元素的分数
# 时间复杂度O(1)
zscore key element

zincrby

# 增加或减少元素的分数
# 时间复杂度O(1)
zincrby key increScore element

zcard

# 返回元素的总个数
# 时间复杂度O(1)
zcard key

zrange

# 返回指定索引范围内的升序元素[是否显示分值]
# 时间复杂度O(logn + m): n指有序集合元素个数, m指获取范围内元素个数
zrange key start end [withscores]

zrangebyscore

# 返回指定分数范围内的升序元素[是否显示分值]
# 时间复杂度O(logn + m): n指有序集合元素个数, m指获取范围内元素个数
zrange key minscore maxscore [withscores]

zcount

# 返回有序集合指定分数范围内的元素个数
# 时间复杂度O(logn + m): n指有序集合元素个数, m指获取范围内元素个数
zcount key minscore maxscore

zremrangebyrank

# 删除指定排名内的升序元素
# 时间复杂度O(logn + m): n指有序集合元素个数, m指删除范围内元素个数
zremrangebyrank key start end

zremrangebyscore

# 删除指定分数内的升序元素
# 时间复杂度O(logn + m): n指有序集合元素个数, m指删除范围内元素个数
zremrangebyscore key minscore maxscore

演示

2020-04-15_002620

2020-04-15_003832

实战

  • 排行榜

2020-04-15_003932

score: timestamp, salecount, followcount

查漏补缺

  • zrevrank
  • zrevrange
  • zrevrangebyscore
  • zinterstore
  • zunionstore

有序集合总结

2020-04-15_004354


   转载规则


《Redis API的使用和理解》 Jiavg 采用 知识共享署名 4.0 国际许可协议 进行许可。
  目录